/*
 * Copyright 2001-2008 Aqris Software AS. All rights reserved.
 * 
 * This program is dual-licensed under both the Common Development
 * and Distribution License ("CDDL") and the GNU General Public
 * License ("GPL"). You may elect to use one or the other of these
 * licenses.
 */
package net.sf.refactorit.refactorings.changesignature.analyzer;


import net.sf.refactorit.classmodel.BinMethod;
import net.sf.refactorit.common.util.MultiValueMap;
import net.sf.refactorit.ui.treetable.BinTreeTableNode;
import net.sf.refactorit.ui.treetable.ParentTreeTableNode;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;


/**
 *
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Company: Aqris AS</p>
 * @author Kirill Buhhalko
 * @version
 */

public abstract class CallNode extends BinTreeTableNode {

  /** Autogenerated proxy constructor. */
  public CallNode(final Object bin) {
    super(bin);
  }

  public static final int SYNTETIC = -1;
  public static final int REC = -2;
  public static final int OWNER = 1;
  public static final int PARENT = 0;
  public static final int CHILD = 2;

  String displayName = null;

  protected MethodsInvocationsMap finder;
  protected int typeD;
  protected MultiValueMap map;

  public String getDisplayName() {

    if (getBin() instanceof BinMethod) {
      if (displayName == null) {
        String name = ((BinMethod) getBin()).getQualifiedName();
        int i = 0;
        for (i = name.length() - 1; i > 0; i--) {
          if (name.charAt(i) == '.') {
            break;
          }
        }

        displayName = "<html>" + name.substring(0,
            i + 1) + "<font color ='red'>" + name.substring(i + 1,
            name.length()) + "(): ";
      }

      return displayName;
    }

    return super.getDisplayName();
  }

  protected abstract void findChilds(MethodsInvocationsMap finder,
      final BinMethod method);

  protected abstract void findParents(MethodsInvocationsMap finder,
      final BinMethod method);

  public void setSelected(boolean selected) {
    try {
      setSelected(selected, true);
    } catch (StackOverflowError e) {e.printStackTrace();
    }
  }

  public void setSelected(boolean selected, boolean flag) {
    if ((typeD == OWNER) || (typeD == SYNTETIC)) {
      selected = true;
    }
    if (typeD == REC) {
      selected = false;
    }

    if (selected && !this.selected && !(typeD == SYNTETIC) && flag) {
      processForEachOtherNodeWithSuchBin(getBin(), selected);
      if (getBin() instanceof BinMethod) {
        List overrides = ((BinMethod) getBin()).
            findAllOverridesOverriddenInHierarchy();
        for (int i = 0; i < overrides.size(); i++) {
          processForEachOtherNodeWithSuchBin(overrides.get(i), false);
        }
      }
    }

    if (selected && !(typeD == SYNTETIC)) {
      if (((ParentTreeTableNode)this).getChildCount() == 0) {
        //try to find new childs
        Object o = this.getBin();

        if (o instanceof BinMethod) {

        } else {
          if (getParent().getBin() instanceof BinMethod) {
            o = getParent().getBin();
          }
        }
        if (o instanceof BinMethod) {
          BinMethod method = (BinMethod) o;
          if (this.typeD == CHILD) {
            findChilds(finder, method);
            this.selected = selected;
          }
          if (this.typeD == PARENT) {
            findParents(finder, method);
            this.selected = selected;
          }
        }
      }

      if (getParent() != null) {
        CallNode parentNode = (CallNode) getParent();
        parentNode.setSelected(selected);
      }
    }

    if (!selected && this.selected && !(typeD == SYNTETIC) && flag) {
      Object ob = map.get(getBin());
      if (ob != null) {
        List l = (List) ob;
        for (int i = 0; i < l.size(); i++) {
          if (l.get(i) != this) {
            ((CallNode) l.get(i)).setSelected(selected, false);
          }
        }
      }
    }

    if (!selected && this.selected && !(typeD == SYNTETIC)) {
      ArrayList list = this.getAllChildren();
      for (int i = 0; i < list.size(); i++) {
        ((CallNode) list.get(i)).setSelected(selected);
      }
    }

    this.selected = selected;

  }

  /**
   * @param selected
   */
  private void processForEachOtherNodeWithSuchBin(final Object bin,
      final boolean selected) {
    Object ob = map.get(bin);
    if (ob != null) {
      List l = (List) ob;
      for (int i = 0; i < l.size(); i++) {
        if (l.get(i) != this) {
          ((CallNode) l.get(i)).setSelected(selected, false);
        }
      }
    }
  }

  public boolean alreadyContains(BinMethod m) {
    if (m.equals(getBin())) {
      return true;
    } else {
      if (getParent() != null) {
        return ((CallNode) getParent()).alreadyContains(m);
      } else {
        return false;
      }
    }
  }

  public static List sort(List list) {
    if (list == null) {
      return null;
    }
    BinMethod[] methods = (BinMethod[]) list.toArray(new BinMethod[list.size()]);
    ArrayList result = new ArrayList(list.size() + 1);

    Arrays.sort(methods, new Comparator() {
      public int compare(Object obj1, Object obj2) {
        BinMethod m1 = (BinMethod) obj1;
        BinMethod m2 = (BinMethod) obj2;
        String str1 = m1.getQualifiedName();
        String str2 = m2.getQualifiedName();
        return str1.compareTo(str2);
      }
    });

    for (int i = 0; i < methods.length; i++) {
      result.add(methods[i]);
    }

    return result;
  }
}
